Vue 中 class 的绑定方式
Vue 的 :class 支持多种写法,渲染器需要全部兼容:
<!-- 1. 字符串 -->
<div class="foo bar"></div>
<!-- 2. 对象语法 -->
<div :class="{ foo: true, bar: false }"></div>
<!-- 3. 数组语法 -->
<div :class="['foo', 'bar']"></div>
<!-- 4. 混合语法 -->
<div :class="['foo', { bar: true }, 'baz']"></div>
html
最终渲染到 DOM 上都是字符串形式的 className。
class 序列化(normalizeClass)
将各种格式的 class 值统一转换为字符串:
function normalizeClassValue(value) {
let res = ''
if (typeof value === 'string') {
res = value
} else if (Array.isArray(value)) {
// 数组:递归序列化每个元素,过滤空值
res = value
.map(normalizeClassValue)
.filter(Boolean)
.join(' ')
} else if (typeof value === 'object' && value !== null) {
// 对象:保留值为真的键
res = Object.entries(value)
.filter(([, val]) => val)
.map(([key]) => key)
.join(' ')
}
return res
}
javascript
转换示例
| 输入 | 输出 |
|---|---|
'foo bar' | 'foo bar' |
['foo', 'bar'] | 'foo bar' |
{ foo: true, bar: false } | 'foo' |
['foo', { bar: true }] | 'foo bar' |
patchProps 中的 class 处理
function patchProps(el, key, prevValue, nextValue) {
if (key === 'class') {
// class 特殊处理:使用 className 而非 setAttribute
el.className = normalizeClassValue(nextValue)
} else if (shouldSetAsProps(el, key, nextValue)) {
// DOM Property
// ... 其他逻辑
} else {
// HTML Attribute
el.setAttribute(key, nextValue)
}
}
javascript
为什么用 className 而非 setAttribute
| 方法 | 性能 | 说明 |
|---|---|---|
el.className = 'foo' | 最优 | 直接设置 DOM property |
el.setAttribute('class', 'foo') | 较慢 | 需要经过 attribute 解析 |
el.classList.add('foo') | 中等 | 适合增量操作,不适合整体替换 |
className 的性能在 JSBench 基准测试中优于 setAttribute 和 classList。
style 的处理
Vue 的 :style 同样支持多种写法:
<!-- 对象语法 -->
<div :style="{ color: 'red', fontSize: '16px' }"></div>
<!-- 数组语法 -->
<div :style="[{ color: 'red' }, { fontSize: '16px' }"></div>
html
style 序列化逻辑
function normalizeStyleValue(value) {
if (typeof value === 'string') return value
if (Array.isArray(value)) {
// 数组:后面对象属性覆盖前面
return value.reduce((acc, item) => {
return { ...acc, ...normalizeStyleValue(item) }
}, {})
}
if (typeof value === 'object' && value !== null) {
// 对象:直接使用
return Object.entries(value)
.map(([key, val]) => `${camelToKebab(key)}: ${val}`)
.join('; ')
}
return ''
}
javascript
处理要点
- 数组中后面的对象属性会覆盖前面同名的属性
- 对象的 key 需要从 camelCase 转换为 kebab-case
- 支持自动添加浏览器前缀(如
-webkit-) - 支持多重值(如
display: [flex, '-webkit-flex'])
总结
渲染器对 class 和 style 的处理本质上是序列化过程——将 Vue 支持的多种数据格式统一转换为 DOM API 期望的字符串格式。这两个属性的共同特点:
- 支持多种输入格式(字符串、对象、数组、混合)
- 最终都需要转换为字符串
- 都有专门的 DOM property(
className、style.cssText)用于高效设置
↑